#include "server.h"
#include "anet.h"
#include "zmalloc.h"

#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <string.h>

#define MAX_ACCEPTS_PER_CALL 1000

static char BUFF[] = "write to client\n";

/*
 * 释放客户端
 */
void freeClient(redisClient *c) {
    listNode *ln;

    /* Free the query buffer */
    sdsfree(c->querybuf);
    c->querybuf = NULL;

    /* Close socket, unregister events, and remove list of replies and
     * accumulated arguments. */
    // 关闭套接字，并从事件处理器中删除该套接字的事件
    if (c->fd != -1) {
        aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
        aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
        close(c->fd);
    }

    /* Remove from the list of clients */
    // 从服务器的客户端链表中删除自身
    if (c->fd != -1) {
        ln = listSearchKey(server.clients,c);
        // redisAssert(ln != NULL);
        listDelNode(server.clients,ln);
    }

    // 释放客户端 redisClient 结构本身
    zfree(c);
}

void writeBUffToClient(aeEventLoop *el, int fd, void *privdata, int mask){
    // redisClient *c = (redisClient*) privdata;
    int wlen;

    wlen = write(fd, BUFF, sizeof(BUFF));
    serverLog(LOG_INFO, "writeBUffToClient wlen=%d\n",wlen);
    //这里删除写事件 是因为默认水平触发  只要对端可写 就会一直触发EPOLLOUT事件，那么writeBUffToClient就会一直被调用 可自行注释看看
    aeDeleteFileEvent(server.el,fd,AE_WRITABLE);
}

/*
 * 读取客户端的查询缓冲区内容
 */
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    redisClient *c = (redisClient*) privdata;
    int nread, readlen;
    size_t qblen;

     //clear空间
    sdsclear(c->querybuf);

    // 读入长度（默认为 16 MB）
    readlen = 100;

    qblen = sdslen(c->querybuf);

    // 为查询缓冲区分配空间
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);

    //因为是水平触发 只要一次性将缓冲区数据读取不来 那就不会再产生 EPOLLIN事件
    nread = read(fd, c->querybuf+qblen, readlen);

    // 读入出错
    if (nread == -1) {
        if (errno == EAGAIN) {
            nread = 0;
        } else {
            serverLog(LOG_WARNING, "Reading from client: %s\n",strerror(errno));
            freeClient(c);
            return;
        }
    // 遇到 EOF
    } else if (nread == 0) {
        serverLog(LOG_INFO, "Client closed connection fd=%d\n",fd);
        freeClient(c);
        return;
    }

    if (nread) {
        // 根据内容，更新查询缓冲区（SDS） free 和 len 属性
        // 并将 '\0' 正确地放到内容的最后
        sdsIncrLen(c->querybuf,nread);
        serverLog(LOG_INFO, "readQueryFromClient read=%d,buf=%s\n", nread,c->querybuf);
    } else {
        // 在 nread == -1 且 errno == EAGAIN 时运行
        return;
    }

    aeCreateFileEvent(server.el, fd, AE_WRITABLE, writeBUffToClient, c);    

    //clear空间
    // sdsclear(c->querybuf);
}

/*
 * 创建一个新客户端
 */
redisClient *createClient(int fd) {

    // 分配空间
    redisClient *c = zmalloc(sizeof(redisClient));

    // 非阻塞
    anetNonBlock(NULL,fd);
    // 禁用 Nagle 算法
    anetEnableTcpNoDelay(NULL,fd);
    // 绑定读事件到事件 loop （开始接收命令请求）
    if (aeCreateFileEvent(server.el,fd,AE_READABLE,
        readQueryFromClient, c) == AE_ERR)
    {
        close(fd);
        zfree(c);
        return NULL;
    }

    // 套接字
    c->fd = fd;
    // 查询缓冲区
    c->querybuf = sdsempty();

    // 如果不是伪客户端，那么添加到服务器的客户端链表中
    if (fd != -1) listAddNodeTail(server.clients,c);
    // 返回客户端
    return c;
}


static void acceptCommonHandler(int fd, int flags) {
    // 创建客户端
    redisClient *c;
    if ((c = createClient(fd)) == NULL) {
        serverLog(LOG_WARNING,
            "Error registering fd event for the new client: %s (fd=%d)",
            strerror(errno),fd);
        close(fd); /* May be already closed, just ignore errors */
        return;
    }

    // 如果新添加的客户端令服务器的最大客户端数量达到了
    // 那么向新客户端写入错误信息，并关闭新客户端
    // 先创建客户端，再进行数量检查是为了方便地进行错误信息写入
    if (listLength(server.clients) > server.maxclients) {
        char *err = "-ERR max number of clients reached\r\n";

        /* That's a best effort error message, don't check write errors */
        if (write(c->fd,err,strlen(err)) == -1) {
            /* Nothing to do, Just to avoid the warning... */
        }
        // 更新拒绝连接数
        freeClient(c);
        return;
    }
}

void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
    char cip[NET_IP_STR_LEN];

    while(max--) {
        cfd = anetTcpAccept(NULL, fd, cip, sizeof(cip), &cport);
        if (cfd == ANET_ERR) {
            if (errno != EWOULDBLOCK)
                serverLog(LL_WARNING,
                    "Accepting client connection");
            return;
        }
        anetCloexec(cfd);
        serverLog(LL_VERBOSE,"Accepted %s:%d\n", cip, cport);
        acceptCommonHandler(cfd,0);
    }
}